<template>
	<div class="shuxie">
		<div class="header">
			<div class="title">
				<span>{{ questIndex }}.写字题</span>
			</div>
		</div>
		<div class="resource">
			<template v-for="(item,index) in question.quest.subQuestion[0].question" :key ="index">
				<template v-if="item.type ==='text'">
					<p v-for="(item1,index1) in item.value" :key = "index1">
						{{ item1 }}
					</p>
				</template>
				<template v-else-if="item.type==='audio'">
					<div class="iconBox">
						<img src="@/assets/audio.png" @click="handlePlay(item.value)">
					</div>
				</template>
				<template v-else-if="item.type==='picture'">
					<div class="iconBox">
						<el-image
							style="width: 150px; height: 150px"
							:src="item.value"
							:preview-src-list="[String(item.value)]"
							:initial-index="0"
							fit="contain"
						/>
					</div>
				</template>
				<template v-else-if="item.type==='video'">
					<div class="iconBox">
						<video controls width="250">
							<source :src="item.value" type="video/webm"/>
						</video>
					</div>
				</template>
			</template>
		</div>

		<div class="xiezi-area" v-if="question">
			<div class="svg-character">
				<canvas
					:id="'writer-canvas'+seq"
					type="2d"
					:height="state.canvasHeight"
					:width="state.canvasWidth"
					@touchstart="handleStart"
					@touchmove="handleMove"
					@touchend="handleEnd"
					@mousedown="handleStart"
					@mousemove="handleMove"
					@mouseup="handleEnd"
				>
				</canvas>
			</div>

			<div class="footer">
				<div class="action-button" @click="showStrokePrompt">提示</div>
				<div class="action-button" @click="clearStrokes">重写</div>
			</div>
		</div>
		<div class="content">
			<el-rate
				v-model="state.star"
				allow-half
				disabled
				show-score
				text-color="#ff9900"
				:score-template="state.star + 'points'"
			/>
			<span class="tips">
				得分：
			</span>
		</div>
	</div>
</template>

<style scoped lang="scss">

</style>
<script setup lang="ts">

import {reactive, toRefs, watch, onMounted, defineProps, PropType, Ref} from "vue";
import Api from "@/api";
import {useDataStore} from "@/stores/useDataStore";


// const pinyin_v1 = require("pinyin-tone");

interface Point {
	x: number;
	y: number;
}

type Stroke = Point[];

interface SVGPath {
	cmd: string;
	pos: number[];
}

type CharSVGPath = SVGPath[];

const props = defineProps({
	//子组件接收父组件传递过来的值
	question: {
		type: Object as PropType<{ quest: {subQuestion: Array<any>,question: Array<any>}, seqNum: number }>,
	},
	questIndex: {
		type: Number
	},
	seq: {
		type: Number
	}

})

const { question,questIndex,seq } = toRefs(props) as { question: Ref<{ quest: { subQuestion: Array<any> ,question: Array<any>}, seqNum: number }>, questIndex: Ref<number>,seq: Ref<number>}

const state = reactive<{
	pinyin: string,
	mp3Url: string,
	constraint: { value: string[]; info: any } | null ,
	writer: any,
	dpr: number, // 1个像素几个点
	character: string,
	canvasCtx: CanvasRenderingContext2D | null,
	canvasHeight: number,
	canvasWidth: number,
	hzSVGPath: CharSVGPath[] , // 汉字本身SVG笔画
	strokes: Stroke[] ,// 用户书写笔画
	points: Point[] , // 每一笔的点
	isDrawing: boolean, // 是否在画
	tempStrokes: Stroke[],// 笔画

	isStrokePromptOpened: boolean, // 是否开启笔画提醒
	star: number | string, // 星星评级
	rewriteNum: number, // 重写次数
	strokeNum: number, // 全部书写过的笔画数（包括对的错的）
	rightStrokeNum: number,
	totalStrokeNum: number,

}>({
	pinyin: "",
	mp3Url: "",


	constraint: null,

	writer: null,

	dpr:  1, // 1个像素几个点
	character: "",
	canvasCtx: null,
	canvasHeight: 300,
	canvasWidth: 300,
	hzSVGPath: [], // 汉字本身SVG笔画
	strokes:  [], // 用户书写笔画
	points: [] , // 每一笔的点
	isDrawing: false, // 是否在画
	tempStrokes: [], // 笔画

	isStrokePromptOpened: false, // 是否开启笔画提醒
	star: 0, // 星星评级
	rewriteNum: 0, // 重写次数
	strokeNum: 0, // 全部书写过的笔画数（包括对的错的）,
	rightStrokeNum: 0,
	totalStrokeNum: 0
});

const userAnswerState = useDataStore();

watch(question, async (val: any) => {
	if (val.value.quest.subQuestion[0].content.value[0]) {
		fetchCharacterPinYin(val.value.quest.subQuestion[0].content.value[0]);
		state.character = val.value.quest.subQuestion[0].content.value[0];
	}
});

const fetchCharacterPinYin =  async(character: string) =>  {
	if (!character) {
		return;
	}
	const query = `keyword=${character}&type=1`;
	const res = await Api.fetchCharacterPinYin(query);
	console.log(res)
	if (res && res.data.code === 200) {
		const firstAudio = res.data.data.result.audioList[0];
		if (!firstAudio) {
			return;
		}
		state.mp3Url = firstAudio.url;
		const tuple = firstAudio.pinyin.split(";");
		if (!tuple || tuple.length < 2) {
			return;
		}
		let pinyin = "";
		if (tuple.length == 3) {
			pinyin = tuple[1];
		} else if (tuple.length == 2) {
			pinyin = tuple[0];
		} else if (tuple.length == 4) {
			pinyin = tuple[0];
		}
		// state.pinyin = putPinyin(`,${pinyin}`);
	}
}


onMounted(async () => {
	if (question?.value?.quest.subQuestion[0].content.value[0]) {
		fetchCharacterPinYin(question.value.quest.subQuestion[0].content.value[0]);
		state.character = question.value.quest.subQuestion[0].content.value[0];
	}
	await fetchCharacterPinYin(question?.value?.quest.subQuestion[0].content.value[0]);

	if (state.canvasCtx) {
		drawBackground();
	} else {
		const canvas = document.querySelector("#writer-canvas"+seq.value) as HTMLCanvasElement;
		if (canvas) {
			state.canvasCtx = canvas.getContext("2d");
			drawBackground();
		}
	}
	await fetchCharecterPositionInfo(state.character);
})

const handlePlay = (source: string) => {
	if (userAnswerState.currentAudio) {
		userAnswerState.currentAudio.pause(); // 暂停当前音频
		userAnswerState.currentAudio = null; // 释放当前音频
	}

	userAnswerState.currentAudio = new Audio(source) as any; // 创建新的 Audio 对象
	userAnswerState.currentAudio?.play(); // 播放音频
}

const fetchCharecterPositionInfo = async (char: string) => {
	const res = await Api.getHZPositionInfo(char);
	console.log(res)
	if ("strokes" in res.data) {
		const strokes = parseHZPOS(res.data.strokes);
		state.hzSVGPath = strokes || [];
		drawChar(state.hzSVGPath);
	}
}

// 画米字格
const drawBackground = () => {
	if (!state.canvasCtx) {
		return;
	}
	const lineLength = 10 * state.dpr;
	const span = 1;

	state.canvasCtx.lineWidth = 1 * state.dpr;
	state.canvasCtx.strokeStyle = "#ddd";

	// 画横线
	let hxX = 0;
	let hxY = (state.canvasHeight * state.dpr) / 2;
	state.canvasCtx.beginPath();
	state.canvasCtx.moveTo(hxX, hxY);

	while (hxX < state.canvasHeight * state.dpr) {
		state.canvasCtx.moveTo(hxX, hxY);
		state.canvasCtx.lineTo(hxX + lineLength, hxY);
		hxX += (lineLength + span) * state.dpr;
	}
	state.canvasCtx.closePath();
	state.canvasCtx.stroke();

	// 竖线
	let sxX = (state.canvasHeight * state.dpr) / 2;
	let sxY = 0;
	state.canvasCtx.beginPath();
	state.canvasCtx.moveTo(sxX, sxY);
	while (sxY < state.canvasHeight * state.dpr) {
		state.canvasCtx.moveTo(sxX, sxY);
		state.canvasCtx.lineTo(sxX, sxY + lineLength);
		sxY += (lineLength + span) * state.dpr;
	}
	state.canvasCtx.closePath();
	state.canvasCtx.stroke();

	// 斜线
	let xxX = 0;
	let xxY = 0;
	state.canvasCtx.beginPath();
	state.canvasCtx.moveTo(xxX, xxY);
	while (xxX < state.canvasHeight * state.dpr && xxY < state.canvasHeight * state.dpr) {
		state.canvasCtx.moveTo(xxX, xxY);
		state.canvasCtx.lineTo(xxX + lineLength, xxY + lineLength);
		xxX += (lineLength + span) * state.dpr;
		xxY += (lineLength + span) * state.dpr;
	}
	state.canvasCtx.closePath();
	state.canvasCtx.stroke();
	xxX = state.canvasHeight * state.dpr;
	xxY = 0;
	state.canvasCtx.beginPath();
	state.canvasCtx.moveTo(xxX, xxY);
	while (xxX > 0 && xxY < state.canvasHeight * state.dpr) {
		state.canvasCtx.moveTo(xxX, xxY);
		state.canvasCtx.lineTo(xxX - lineLength, xxY + lineLength);
		xxX -= (lineLength + span) * state.dpr;
		xxY += (lineLength + span) * state.dpr;
	}
	state.canvasCtx.closePath();
	state.canvasCtx.stroke();
}

// 获取并处理汉字坐标信息
const parseHZPOS = (strokes: string[]) => {
	if (!state.canvasCtx) {
		return;
	}
	const {width} = state.canvasCtx.canvas;
	const ratio = width / 1000 / state.dpr;

	const returnStrokes = strokes.map((posStr) => {
		const posArr = posStr
			.split(/[A-Z]/)
			.filter((posItem) => posItem !== "")
			.map((posItem) => {
				return posItem
					.trim()
					.split(" ")
					.map((num, idx) => {
						let ret = parseInt(num) * ratio;

						if (idx % 2 !== 0) {
							// 偶数，即y坐标
							ret = width / state.dpr - ret - 35; // 35是什么
						} else {
							ret = ret - 5; // 5是什么
						}

						return ret;
					});
			});
		const cmdArr = posStr.split(/[\d\s\-]+/);

		const stroke = [];
		for (let idx = 0; idx < cmdArr.length; idx += 1) {
			stroke.push({
				cmd: cmdArr[idx],
				pos: idx < posArr.length ? posArr[idx] : [],
			});
		}

		return stroke;
	});

	return returnStrokes;
}



const putPinyin = (pinyinEle: string) => {
	let [hanzi, pinyinTone] = pinyinEle.split(",");
	if (pinyinTone) {
		// pinyinTone = toneConvert(pinyinTone);
		// pinyinTone = pinyin_v1(pinyinTone);
		return pinyinTone;
	} else {
		return pinyinEle;
	}
}

const drawChar = (charSVGPath: CharSVGPath[]) => {
	if (!state.canvasCtx) {
		return;
	}

	state.canvasCtx.lineWidth = 1 * state.dpr;
	state.canvasCtx.lineCap = "round";
	state.canvasCtx.lineJoin = "round";

	charSVGPath.forEach((stroke: SVGPath[], idx: number) => {
		if (!state.canvasCtx) {
			return;
		}

		if (idx < state.strokes.length) {
			// 用户已经写过的笔划
			state.canvasCtx.strokeStyle = "#000";
			state.canvasCtx.fillStyle = "#000";
		} else if (state.isStrokePromptOpened && idx === state.strokes.length) {
			// 需要提醒的笔划
			state.canvasCtx.strokeStyle = "#ff6574";
			state.canvasCtx.fillStyle = "#ff6574";
		} else {
			// 未写的笔划
			state.canvasCtx.strokeStyle = "#ccc";
			state.canvasCtx.fillStyle = "#ccc";
		}

		state.canvasCtx.beginPath();

		stroke.forEach((strkItem: SVGPath) => {
			if (!state.canvasCtx) {
				return;
			}

			const p = strkItem.pos.map((num: number) => num * state.dpr);

			switch (strkItem.cmd) {
				case "M":
					state.canvasCtx.moveTo(p[0], p[1]);
					break;
				case "L":
					state.canvasCtx.lineTo(p[0], p[1]);
					break;
				case "Q":
					state.canvasCtx.quadraticCurveTo(p[0], p[1], p[2], p[3]);
					break;
				case "C":
					state.canvasCtx.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
					break;
				case "Z":
					state.canvasCtx.fill();
					state.canvasCtx.closePath();
					break;
			}
		});
		state.canvasCtx.stroke();
	});
}

// 评判一笔的书写情况
const  doJudgeStroke =  async(points: any[]) => {
	const xyData = points.map((p) => `${parseInt(p.x)}/${parseInt(p.y)}`).join("/");

	const formData = new FormData();
	formData.append("zi", state.character);
	formData.append("no", state.strokes.length + "");
	formData.append("xy", xyData);

	const query = `zi=${state.character}&no=${state.strokes.length}&xy=${xyData}`;
	const ret = await Api.judgeStroke(query);
	console.log(ret,'ret')
	return typeof ret.data === "object" && ret.data.iserr === "false" && ret.data.data === "1";
}

const getPoint = (e: MouseEvent) => {
	if (e) {
		return {x: e.offsetX, y: e.offsetY};
	} else {
		return {
			x: 0,
			y: 0,
		};
	}
}

const draw = (point: { x: number; y: number }) => {
	if (!state.canvasCtx) {
		return;
	}

	state.canvasCtx.lineWidth = 10 * state.dpr;
	state.canvasCtx.strokeStyle = "#000";
	state.canvasCtx.lineCap = "round";
	state.canvasCtx.lineJoin = "round";

	state.canvasCtx.lineTo(point.x * state.dpr, point.y * state.dpr);
	state.canvasCtx.stroke();
	state.canvasCtx.moveTo(point.x * state.dpr, point.y * state.dpr);

	state.points.push(point);
}

const handleStart = (e: MouseEvent) => {
	if (!state.canvasCtx) {
		return;
	}
	state.isDrawing = true;

	let {x, y} = getPoint(e);

	state.canvasCtx.beginPath();
	state.canvasCtx.moveTo(x * state.dpr, y * state.dpr);
	state.points.push({x, y});
}
const handleMove = (e: MouseEvent) =>{
	if (!state.isDrawing) {
		return;
	}

	if (!state.canvasCtx) {
		return;
	}

	draw(getPoint(e));
}

const handleEnd =async (e: MouseEvent) => {
	if (!state.canvasCtx) {
		return;
	}

	state.isDrawing = false;
	state.strokeNum += 1;

	const ret = await doJudgeStroke(state.points);
	if (ret) {
		state.strokes.push(state.points);
	}

	// 计算分数
	const rightScore = (state.strokes.length / state.hzSVGPath.length) * 5;
	const wrongStrokeNum = state.strokeNum > state.strokes.length ? state.strokeNum - state.strokes.length : 0;
	const penalty = wrongStrokeNum / state.hzSVGPath.length;
	state.star = rightScore - penalty;
	state.star = state.star > 0 ? state.star.toFixed(2) : 0; // 防止为负
	state.totalStrokeNum = state.hzSVGPath.length;
	state.rightStrokeNum = state.strokes.length;
	// TODO:保存书写笔画
	saveWriteResult();
	// 刷新画布，会自动把已经成功写好的涂黑
	refreshCanvas();
	state.points = [];
}


const saveWriteResult = () => {
	const score = state.star;
	const info = {
		allStrokeNum: state.strokeNum,
		rewriteNum: state.rewriteNum,
		ziStrokeNum: state.totalStrokeNum,
		rightStrokeNum: state.rightStrokeNum,
	}

	const payload = {
		template: question?.value?.quest.subQuestion[0].template,
		score: score,
		info: info,
		zi: state.character,
		strokes: state.strokes
	};
	userAnswerState.setUserAnswer(payload, seq.value-1);
	console.log(userAnswerState.userAnswer)
}

// 刷新画布
const refreshCanvas = () => {
	if (!state.canvasCtx) {
		return;
	}
	// 刷新canvas
	state.canvasCtx.clearRect(0, 0, state.canvasWidth * state.dpr, state.canvasHeight * state.dpr);
	drawBackground();
	drawChar(state.hzSVGPath);
}

const showStrokePrompt = () => {
	state.isStrokePromptOpened = !state.isStrokePromptOpened;
	drawChar(state.hzSVGPath);
}

const clearStrokes = () => {
	state.strokes = [];
	state.strokeNum = 0;
	state.star = 0;
	refreshCanvas();
}
</script>


<style lang="scss" scoped>
.shuxie {
	img {
		width: 30px;
		height: 30px;
		object-fit: contain;
	}
	.resource {
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		.iconBox {
			margin-top: 20px;
		}

	}
	.header {
		.title {
			font-size: 20px;
			font-weight: 500;
			margin-bottom: 10px;
			color: #6a727f;
			.dot {
				font-weight: 700;
				color: #5189dd;
				margin-right: 5px;
			}
		}
	}
	.content {
		width: 95%;
		position: relative;
		height: 50px;
		display: flex;
		justify-content: center;
		align-items: center;
		border: 1px solid #dcdfe6;
		text-indent: 0;
		margin: 0 auto;
		.audioAnswer {
			font-size: 30px;
			cursor: pointer;
		}
		.tips {
			font-size: 20px;
			color: #7c7787;
			position: absolute;
			left: 0;
			top: 0;
		}
	}
	.xiezi-area {
		display: flex;
		flex-direction: column;
		//align-items: center;
		width: 50%;
		margin: 0 auto;


		.svg-character {
			position: relative;
			text-align: center;

			.writer-canvas {
				margin-top: 10px;
				border: 1px solid black;
				box-sizing: border-box;
			}

			.character {
				position: absolute;
				top: 0;
				left: 0;
				right: 0;
				bottom: 0;

				display: flex;
				justify-content: center;
				align-items: center;

				color: rgb(221, 221, 221);
				font-size: 280px;
				font-family: cursive;
			}
		}

		.sound-area {
			font-size: 18px;
			display: flex;
			justify-content: space-between;
			align-items: center;

			.right-audio {
				display: flex;
				justify-content: flex-end;
				align-items: center;
			}

			.audio-answer-type {
				box-sizing: border-box;
				min-width: 40px;
				width: 40px;
				height: 40px;
				line-height: 40px;

				position: relative;
				text-align: center;
				border-radius: 100px;
				color: #fff;
				background-color: #9cb8e4;

				display: flex;
				justify-content: center;
				align-items: center;

				&.listen {
					background-color: #00557f;
					margin-right: 5px;
				}
			}
		}

		.answer-type {
			.text-answer-area {
				min-height: 24px;
				font-size: 18px;
				margin: 0 auto;
				padding: 5px;

				border-radius: 4px;
				background-color: #fff;

				&:empty:not(:focus)::after {
					content: attr(data-text);
					color: rgba(0, 0, 0, 0.15);
				}
			}

			.audio-answer-type,
			.picture-answer-area {
				margin: 5px 10px;
				box-sizing: border-box;
				min-width: 50px;
				width: 50px;
				height: 50px;
				line-height: 50px;

				position: relative;
				text-align: center;
				border-radius: 100px;
				color: #fff;
				background-color: #9cb8e4;
			}
		}

		.character-svg {
			margin-top: 20px;
			box-sizing: border-box;
			border: 2px solid rgb(154, 154, 154);
		}

		.footer {
			margin-top: 18px;
			margin-bottom: 20px;
			display: flex;
			justify-content: space-between;
			align-items: center;

			.action-button {
				height: 32px;
				width: 80px;
				border-radius: 4px;
				background-color: rgb(162, 183, 224);
				display: flex;
				justify-content: center;
				align-items: center;
				color: white;
				font-weight: bold;
			}
		}
	}
}
</style>